#include <stdio.h>
#include <stdlib.h>
#include <alsa/asoundlib.h>
#include <signal.h>
#include <cstdio>
#include <thread>
#include "UacIn.h"
#include "log.h"
#include "UacUtil.h"
#include <pthread.h>

namespace Usbstream {
    std::mutex UacIn::sm_UacInLock;

UacIn::UacIn(int soundnum){
	LOG_PrintError("UacIn(int soundnum)\n");
	m_soundnum = soundnum;
	m_uacininit = false;
	initUacIn();
}

bool UacIn::initUacIn(){
	m_buffer = NULL;
	m_bufferSize = 0;
	m_exitOldThread = false;
	// Get parameters for this device
	if(!UacUtil::parseDeviceParam(m_soundnum, 1, m_supportParam)){
		LOG_PrintError("get device params faild soundnum:%d\n", m_soundnum);
		return false;
	}
	m_used = true;
	return true;
}

UacIn::~UacIn(){
	deInitUacIn();
}

bool UacIn::deInitUacIn(){
	stopStream();
	//Reclaim object resources
#if 0
	int err;
	if ((err = snd_pcm_close(m_captureHandle)) < 0) {
		LOG_PrintError("close alsa device faild (%s)\n", snd_strerror(err));
		return false;
	}
#endif
	m_used = false;
	m_supportParam.clear();
	m_soundnum = -1;
	m_usbPort = "";
	m_channels = 0;
	m_format = "";
	m_rates = 0;
	m_bufferSize = 0;
	return true;
}

bool UacIn::getSupportParams(Json::Value &params){
	if(m_supportParam.size() == 0){
		LOG_PrintError("cannot get params soundnum:%d\n", m_soundnum);
		return false;
	}
	params = m_supportParam;
	return true;
}

bool UacIn::setParams(const Json::Value params){
	if(m_uacininit){
		return false;
	}
	Json::Value ret;
	Json::Value support;
	Json::Value::Members paramsMember;
	Json::Value::Members supportMember;
	int err;

	if(m_supportParam.size() == 0){
		LOG_PrintError("no support params soundnum:%d\n", m_soundnum);
		return false;
	}

	support  = m_supportParam;

	paramsMember = params.getMemberNames();
	for(auto paramsit = paramsMember.begin(); paramsit != paramsMember.end(); paramsit++){
		//You need to verify whether the parameter is supported. If the parameter is not supported, the system returns
		if(!support.isMember(*paramsit)){
			LOG_PrintError("cannot support the params %s\n", (*paramsit).c_str());
			return false;
		}
		ret[*paramsit] = params[*paramsit].asString();
	}
	//The missing parameters use the supported default parameters
	supportMember = support.getMemberNames();
	for(auto supportit = supportMember.begin(); supportit != supportMember.end(); supportit++){
		if(!params.isMember(*supportit)){
			ret[*supportit] = support[*supportit].asString();
		}
	}




	string name = devnamePrefix + to_string(m_soundnum);
	LOG_PrintInfo("UacIn::params json:%s\n", ret.toStyledString().c_str());
	if ((err = snd_pcm_open(&m_captureHandle, name.c_str(), SND_PCM_STREAM_CAPTURE, 0)) < 0) {
		LOG_PrintError("cannot open audio device %s (%s)\n", name.c_str(), snd_strerror(err));
		return false;
	}

	if ((err = snd_pcm_hw_params_malloc(&m_hwParams)) < 0){
		LOG_PrintError("cannot allocate hardware parameter structure (%s)\n", snd_strerror(err));
		return false;
	}

	if ((err = snd_pcm_hw_params_any(m_captureHandle, m_hwParams)) < 0){
		LOG_PrintError("cannot initialize hardware parameter structure (%s)\n", snd_strerror(err));
		return false;
	}

	if ((err = snd_pcm_hw_params_set_access(m_captureHandle, m_hwParams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
		LOG_PrintError("cannot set access type (%s)\n", snd_strerror(err));
		return false;
	}

	//Set the format, because the string and enumeration conversion is more troublesome, the first to write a format
	if ((err = snd_pcm_hw_params_set_format(m_captureHandle, m_hwParams, SND_PCM_FORMAT_S16_LE)) < 0) {
		LOG_PrintError("cannot set sample format (%s)\n", snd_strerror(err));
		return false;
	}

#if 1
	int dir = SND_PCM_STREAM_PLAYBACK;
	unsigned int sample_rate = stoi(ret["Rates"].asString());
	int val = sample_rate / 1000 * 16;
	LOG_PrintInfo("set Rates: %d\n", val);
	err = snd_pcm_hw_params_set_period_size(m_captureHandle, m_hwParams, val, dir);
	if (err < 0)
	{
		printf("[Func]:%s [Line]:%d info error: %s", __FUNCTION__, __LINE__, snd_strerror(err));
		return false;
	}
#endif
	//Set sampling rate
	if ((err = snd_pcm_hw_params_set_rate_near(m_captureHandle, m_hwParams, &sample_rate, 0)) < 0) {
		LOG_PrintError("cannot set sample rate (%s)\n", snd_strerror(err));
		return false;
	}
	m_rates = sample_rate;
	//Set the number of channels
	unsigned int channel = stoi(ret["Channels"].asString());
	if ((err = snd_pcm_hw_params_set_channels(m_captureHandle, m_hwParams, channel))  < 0) {
		LOG_PrintError("cannot set channel count (%s)\n", snd_strerror(err));
		return false;
	}
	m_channels = channel;
	//Write these parameters to the hardware
	if ((err = snd_pcm_hw_params(m_captureHandle, m_hwParams)) < 0) {
		LOG_PrintError("cannot set parameters (%s)\n", snd_strerror(err));
		return false;
	}

	snd_pcm_hw_params_free(m_hwParams);
	if ((err = snd_pcm_prepare(m_captureHandle)) < 0) {
		LOG_PrintError("cannot prepare audio interface for use (%s)\n", snd_strerror(err));
		return false;
	}

	//Calculate the size of the buffer and allocate the memory of the buffer
	m_bufferSize = m_rates / 1000 * 16;
	m_buffer = malloc(m_bufferSize * (snd_pcm_format_width(SND_PCM_FORMAT_S16_LE)/8)  * m_channels);
	m_uacininit = true;
	return true;
}



bool UacIn::startStream(callback_t cb){
	//Repeat the initialization judgment
	if(m_running){
		LOG_PrintError("the stream is starting cannot restart/n");
		return false;
	}
	//Initialize the flag bit
	m_running = true;
	//Start stream processing
	processStream(cb);
	return true;
}

void UacIn::processStream(callback_t cb){
	auto lam = [this, cb](){
		unsigned int err;
		while(1){
			if(!m_used){
				LOG_PrintInfo("UacIn::processStream(){ stop\n");
				break;
			}
			if(m_running){
				if ((err = snd_pcm_readi(m_captureHandle, m_buffer, m_bufferSize)) != m_bufferSize) {
					std::this_thread::sleep_for(std::chrono::microseconds(20));
					if(m_index++ == 620){
						m_index = 0;
						LOG_PrintError("read from audio interface failed (%s)\n", snd_strerror(err));
					}
					continue;
				}
				processBuffer(m_buffer, m_bufferSize * (snd_pcm_format_width(SND_PCM_FORMAT_S16_LE)/8)  * m_channels, cb);
			}else{
				//exit
				//std::this_thread::sleep_for(std::chrono::milliseconds(32));
				m_exitOldThread = true;
				break;
			}
		}
	};

	thread stream_process(lam);
	// Gets the underlying pthread_t handle
    pthread_t pthreadHandle = stream_process.native_handle();
    // set thread name
    pthread_setname_np(pthreadHandle, "uac_in_thread");
	stream_process.detach();
}

bool UacIn::processBuffer(void *buffer, int length, callback_t cb){
	//Calls callbacks that process the data
	if(cb == nullptr){
		LOG_PrintError("cnanot set the stream process callback usbport:%s\n", m_usbPort.c_str());
		return false;
	}
	//LOG_PrintWarn("sample_length:%d\", (snd_pcm_format_width(SND_PCM_FORMAT_S16_LE)/8)  * m_channels);
	cb(buffer, length);
	return true;
}


bool UacIn::stopStream(){
	if(!m_uacininit){
		return true;
	}

	m_running = false;
	while(!m_exitOldThread){
		std::this_thread::sleep_for(std::chrono::milliseconds(1));
	}
	m_exitOldThread = false;

	int err = 0;
	if ((err = snd_pcm_drain(m_captureHandle)) != 0) {
		LOG_PrintError("close alsa device faild (%s)\n", snd_strerror(err));
		return false;
	}
	
	if ((err = snd_pcm_close(m_captureHandle)) != 0) {
		LOG_PrintError("close alsa device faild (%s)\n", snd_strerror(err));
		return false;
	}
	if(m_buffer != NULL){
		LOG_PrintWarn("stopStream  free start\n");
		free(m_buffer);
		m_buffer = NULL;
		LOG_PrintWarn("stopStream  free end\n");
	}
	m_uacininit = false;
	return true;
}


}
